package com.xdf.wxbebarrel.utils;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xdf.wxbebarrel.model.WxAccessToken;
import com.xdf.wxbebarrel.model.WxJsApiConfig1;
import com.xdf.wxbebarrel.model.WxJsApiTicket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

@Service
@Slf4j
public class WxJsApiOfficial {

    @Autowired
    WxAccessToken wxAccessToken;

    private static final String TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
    private static final ObjectMapper mapper = new ObjectMapper();
    private WxJsApiTicket ticket;

    // 获取多个配置对象：两头光，有尾巴，有协议，两头有
    public List<WxJsApiConfig1> getConfigs(String url) throws IOException {
        String url1 = "www.xdfznh.club/wxpadfe/wxscan";
        String url2 = "www.xdfznh.club/wxpadfe/wxscan/";
        String url3 = url;
        String url4 = "https://www.xdfznh.club/wxpadfe/wxscan/";

        List<WxJsApiConfig1> list = new ArrayList<>();
        list.add(getConfig(url1));
        list.add(getConfig(url2));
        list.add(getConfig(url3));
        list.add(getConfig(url4));

        return list;
    }

    public WxJsApiConfig1 getConfig(String url) throws IOException {
        log.info("官方案例修改来的，构建 jsapi 配置对象");
        WxJsApiTicket ticket = getWxJsApiTicket();// 从缓存获取票据，过期则申请新的
        if( ticket == null ) {
            log.info("请求jsapi 票据时失败！");
            return null;
        }
        log.info("用于构建config的url:" + url);
        Map<String,String> ret = sign(ticket.getTicket(),url);
        log.info("map类型参数对象是：" + ret.toString());
        WxJsApiConfig1 config = map2Config(ret);
        log.info("JavaBean配置对象是：" + config.toString());
        return config;
    }

    public WxJsApiTicket getWxJsApiTicket() throws IOException {
        if( ticket != null && ticket.isExpired() == false ) {
            log.info("从缓存中获取了票据："+ticket.toString());
            return ticket;// 没有过期则返回缓存的票据
        }

        String accesstoken = wxAccessToken.getAccessToken();// 单例对象中缓存有 access_token，过期的会重新申请
        String url = TICKET_URL.replace("ACCESS_TOKEN",accesstoken);
        String jsonString = HttpUtils.sendGet(url);

        if( jsonString.length()<=0 ){
            log.info("使用token：" + accesstoken + "请求获得jsapi票据返回了空字符串！");
            return null;
        }

        JavaType jvt = mapper.getTypeFactory().constructParametricType(HashMap.class,String.class,String.class);
        Map<String,String> jsonObject = mapper.readValue(jsonString, jvt);
        if( jsonObject == null ){
            log.info("jsapi票据返回的字符串：" + jsonString + "，转换为Map类型后得到了空对象！");
            return null;
        }

        Integer errcode = Integer.parseInt(jsonObject.get("errcode"));
        String errmsg = jsonObject.get("errmsg");
        String ticketString = jsonObject.get("ticket");
        Long expires_in = Long.parseLong(jsonObject.get("expires_in"));

        // 实例化时就开始计时，防止过期
        ticket = new WxJsApiTicket(errcode,errmsg,ticketString,expires_in);
        log.info("构建出新jsapi票据是：" + ticket.toString());

        return ticket;
    }

    public WxJsApiConfig1 map2Config(Map<String,String> map){
        WxJsApiConfig1 config = new WxJsApiConfig1(map.get("timestamp"),map.get("nonceStr"),map.get("signature"));
        return config;
    }

    public Map<String, String> sign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = getNonceStr();
        String timestamp = getTimestamp();
        String string1;
        String signature = "";

        //注意这里参数名必须全部小写，且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + nonce_str +
                "&timestamp=" + timestamp +
                "&url=" + url;
        log.info("拼接的参数字符串：" + string1);

        try
        {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        }
        catch (NoSuchAlgorithmException e)
        {
            e.printStackTrace();
            log.info("进行SHA-1加密时出现异常");
        }
        catch (UnsupportedEncodingException e)
        {
            e.printStackTrace();
            log.info("进行SHA-1加密时出现异常");
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }


    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    private static String getNonceStr() {
        return UUID.randomUUID().toString();
    }

    private static String getTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

}
